<template>
    <div class="pe-transfer">
        <div class="pe-transfer-content">
            <div class="pe-transfer-header">
                <Checkbox v-model="checkAll1" value="1" :label="titles[0]" :indeterminate="leftIndeterminate"
                    @change="changeLeft" />
                <div class="pe-transfer-header-font">{{ leftValue.length }}/{{ leftData.length }}</div>
            </div>
            <div v-if="filterable" class="pe-transfer-search">
                <Input v-model="leftSearch" size="small" :placeholder="filterPlaceholder" />
            </div>
            <div class="pe-transfer-main">
                <CheckboxGroup v-show="leftData.length > 0" v-model="leftValue" @change="changeLeft1">
                    <Checkbox v-for="item in leftList" class="pe-transfer-checkbox" :disabled="item.disabled"
                        :value="item.key">
                        <slot :row="item">
                            <span>{{ item.label }}</span>
                        </slot>
                    </Checkbox>
                </CheckboxGroup>
                <div v-show="leftData.length === 0" class="pe-no-data">暂无数据</div>
            </div>
            <div v-if="useSlots()['left-footer'] || useSlots()['right-footer']" class="pe-transfer-footer">
                <slot name="left-footer"></slot>
            </div>
        </div>
        <Button type="primary" class="pe-transfer-left-btn" :disabled="rightValue.length === 0" @click="toLeft">
            <i class="pe-icon">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
                    <path fill="currentColor"
                        d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z">
                    </path>
                </svg>
            </i>
        </Button>
        <Button type="primary" class="pe-transfer-right-btn" :disabled="leftValue.length === 0" @click="toRight">
            <i class="pe-icon">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
                    <path fill="currentColor"
                        d="M340.864 149.312a30.592 30.592 0 0 0 0 42.752L652.736 512 340.864 831.872a30.592 30.592 0 0 0 0 42.752 29.12 29.12 0 0 0 41.728 0L714.24 534.336a32 32 0 0 0 0-44.672L382.592 149.376a29.12 29.12 0 0 0-41.728 0z">
                    </path>
                </svg>
            </i>
        </Button>
        <div class="pe-transfer-content">
            <div class="pe-transfer-header">
                <Checkbox v-model="checkAll2" value="1" :label="titles[1]" :indeterminate="rightIndeterminate"
                    @change="changeRight" />
                <div class="pe-transfer-header-font">{{ rightValue.length }}/{{ rightData.length }}</div>
            </div>
            <div v-if="filterable" class="pe-transfer-search">
                <Input v-model="rightSearch" size="small" :placeholder="filterPlaceholder" />
            </div>
            <div class="pe-transfer-main">
                <CheckboxGroup v-show="rightData.length > 0" v-model="rightValue" @change="changeRight1">
                    <Checkbox v-for="item in rightList" class="pe-transfer-checkbox" :disabled="item.disabled"
                        :value="item.key">
                        <slot :row="item">
                            <span>{{ item.label }}</span>
                        </slot>
                    </Checkbox>
                </CheckboxGroup>
                <div v-show="rightData.length === 0" class="pe-no-data">暂无数据</div>
            </div>
            <div v-if="useSlots()['left-footer'] || useSlots()['right-footer']" class="pe-transfer-footer">
                <slot name="right-footer"></slot>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
import { computed, ref, toRaw, watch, useSlots } from "vue"
import Checkbox from "../checkbox/checkbox.vue";
import CheckboxGroup from "../checkbox/checkbox-group.vue";
import Button from "../button/button.vue";
import Input from "../input/input.vue";

// 传值
const modelValue = defineModel<Array<string | number>>({ default: [] })

// 是否选择全部
const checkAll1 = ref<boolean>(false)
const checkAll2 = ref<boolean>(false)
// 不确定状态
const leftIndeterminate = ref<boolean>(false)
const rightIndeterminate = ref<boolean>(false)

/**
 * @param key 唯一标识
 * @param label 显示的文本
 * @param disabled 是否禁用
 */
interface Option {
    key: number | string
    label: string
    disabled: boolean
}

/**
 * @param data Transfer 的数据源
 * @param filterable 是否可搜索
 * @param filter-placeholder 搜索框占位符
 * @param target-order 右侧列表元素的排序策略： 若为 original，则保持与数据源相同的顺序； 若为 push，则新加入的元素排在最后； 若为 unshift，则新加入的元素排在最前
 * @param titles 自定义列表标题
 * @param left-default-checked 初始状态下左侧列表的已勾选项的 key 数组
 * @param right-default-checked 初始状态下右侧列表的已勾选项的 key 数组
 */
interface Props {
    data?: Array<Option>,
    filterable?: boolean,
    filterPlaceholder?: string,
    targetOrder?: 'original' | 'push' | 'unshift',
    titles?: [string, string],
    leftDefaultChecked?: Array<string | number>,
    rightDefaultChecked?: Array<string | number>
}

const props = withDefaults(defineProps<Props>(), {
    data: () => [],
    filterable: true,
    targetOrder: "original",
    titles: () => ["List1", "List2"],
    leftDefaultChecked: () => [],
    rightDefaultChecked: () => []
})

const emits = defineEmits(['change', 'left-check-change', 'right-check-change'])

// 左侧数据
const leftData = ref<Array<Option>>([])
const leftValue = ref<Array<string | number>>([])

// 右侧数据
const rightData = ref<Array<Option>>([])
const rightValue = ref<Array<string | number>>([])
watch(props, newProps => {
    leftData.value = []
    rightData.value = []
    newProps.data.forEach(item => {
        if (modelValue.value.includes(item.key)) {
            rightData.value.push(item)
        } else {
            leftData.value.push(item)
        }
    })
    leftValue.value = newProps.leftDefaultChecked
    rightValue.value = newProps.rightDefaultChecked
}, { immediate: true })

const changeLeft = (val: boolean) => {
    let arr: Array<string | number> = []
    let arr1: Array<Option> = []
    leftList.value.forEach(item => {
        if (!item.disabled) {
            arr.push(item.key)
            arr1.push(item)
        }
    })
    leftValue.value = val ? arr : []
    leftIndeterminate.value = false
    emits("left-check-change", val ? arr1 : [], val ? arr : [])
}

const changeLeft1 = (val: Array<string | number>) => {
    let count: number = 0
    let count1: number = 0
    leftList.value.forEach(item => {
        if (!item.disabled) {
            count++
            if (val.includes(item.key)) {
                count1++
            }
        }
    })
    checkAll1.value = count1 === count
    leftIndeterminate.value = count1 > 0 && count1 < count
    let arr: Array<Option> = []
    leftData.value.forEach(item => {
        if (val.includes(item.key)) {
            arr.push(item)
        }
    })
    emits("left-check-change", arr, val)
}

const changeRight = (val: boolean) => {
    let arr: Array<string | number> = []
    let arr1: Array<Option> = []
    rightList.value.forEach(item => {
        if (!item.disabled) {
            arr.push(item.key)
            arr1.push(item)
        }
    })
    rightValue.value = val ? arr : []
    rightIndeterminate.value = false
    emits("right-check-change", val ? arr1 : [], val ? arr : [])
}

const changeRight1 = (val: Array<string | number>) => {
    let count: number = 0
    let count1: number = 0
    rightList.value.forEach(item => {
        if (!item.disabled) {
            count++
            if (val.includes(item.key)) {
                count1++
            }
        }
    })
    checkAll2.value = count1 === count
    rightIndeterminate.value = count1 > 0 && count1 < count
    let arr: Array<Option> = []
    rightData.value.forEach(item => {
        if (val.includes(item.key)) {
            arr.push(item)
        }
    })
    emits("right-check-change", arr, val)
}

const toLeft = () => {
    let arr: Array<Option> = []
    let arr1: Array<Option> = []
    if (props.targetOrder === 'push') {
        leftData.value.forEach(item => {
            arr.push(item)
        })
        props.data.forEach(item => {
            if (rightValue.value.includes(item.key)) {
                arr.push(item)
            }
        })
    } else if (props.targetOrder === 'unshift') {
        leftData.value.forEach(item => {
            arr.push(item)
        })
        props.data.forEach(item => {
            if (rightValue.value.includes(item.key)) {
                arr.unshift(item)
            }
        })
    } else {
        props.data.forEach(item => {
            if (rightValue.value.includes(item.key)) {
                arr.push(item)
                return
            }
            for (let item1 of leftData.value) {
                if (item1.key === item.key) {
                    arr.push(item)
                    break
                }
            }
        })
    }
    props.data.forEach(item => {
        if (!rightValue.value.includes(item.key)) {
            for (let item1 of rightData.value) {
                if (item1.key === item.key) {
                    arr1.push(item)
                    break
                }
            }
        }
    })
    leftData.value = arr
    rightData.value = arr1
    rightValue.value = []
    leftIndeterminate.value = false
    rightIndeterminate.value = false
    checkAll1.value = false
    checkAll2.value = false
    let arr2: Array<string | number> = []
    arr1.forEach(item => {
        arr2.push(item.key)
    })
    modelValue.value = arr2
    emits("change", arr1)
}

const toRight = () => {
    let arr: Array<Option> = []
    let arr1: Array<Option> = []
    if (props.targetOrder === 'push') {
        rightData.value.forEach(item => {
            arr.push(item)
        })
        props.data.forEach(item => {
            if (leftValue.value.includes(item.key)) {
                arr.push(item)
            }
        })
    } else if (props.targetOrder === 'unshift') {
        rightData.value.forEach(item => {
            arr.push(item)
        })
        props.data.forEach(item => {
            if (leftValue.value.includes(item.key)) {
                arr.unshift(item)
            }
        })
    } else {
        props.data.forEach(item => {
            if (leftValue.value.includes(item.key)) {
                arr.push(item)
                return
            }
            for (let item1 of rightData.value) {
                if (item1.key === item.key) {
                    arr.push(item)
                    break
                }
            }
        })
    }
    props.data.forEach(item => {
        if (!leftValue.value.includes(item.key)) {
            for (let item1 of leftData.value) {
                if (item1.key === item.key) {
                    arr1.push(item)
                    break
                }
            }
        }
    })
    rightData.value = arr
    leftData.value = arr1
    leftValue.value = []
    leftIndeterminate.value = false
    rightIndeterminate.value = false
    checkAll1.value = false
    checkAll2.value = false
    let arr2: Array<string | number> = []
    arr.forEach(item => {
        arr2.push(item.key)
    })
    modelValue.value = arr2
    emits("change", arr)
}

// 搜索
const leftSearch = ref<string>("")
const rightSearch = ref<string>("")

// 计算搜索后的值
const leftList = computed(() => {
    const list = toRaw(leftData.value.filter(item => item.label.toLocaleLowerCase().includes(leftSearch.value.toLocaleLowerCase())))
    let count: number = 0
    let count1: number = 0
    list.forEach(item => {
        if (!item.disabled) {
            count++
            if (leftValue.value.includes(item.key)) count1++
        }
    })
    checkAll1.value = count === count1
    leftIndeterminate.value = count1 > 0 && count1 < count
    return list
})

const rightList = computed(() => {
    const list = toRaw(rightData.value.filter(item => item.label.toLocaleLowerCase().includes(rightSearch.value.toLocaleLowerCase())))
    let count: number = 0
    let count1: number = 0
    list.forEach(item => {
        if (!item.disabled) {
            count++
            if (rightValue.value.includes(item.key)) count1++
        }
    })
    checkAll2.value = count === count1
    rightIndeterminate.value = count1 > 0 && count1 < count
    return list
})
</script>

<script lang="ts">
export default {
    name: "pe-transfer"
}
</script>
